package tunnel

import (
	"errors"
	log "github.com/cihub/seelog"
	"github.com/songgao/water"
	"net"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"tunnel/common/config"
	"tunnel/utils"
)

//
// CreateIFace
// @Description:
// @param deviceType
// @return dev
// @return err
//
func CreateIFace(deviceType water.DeviceType) (dev *water.Interface, err error) {
	c := water.Config{
		DeviceType: deviceType,
	}
	d, err := water.New(c)
	if err != nil {
		return nil, err
	}
	log.Info("network interface created with mac [", GetMacByInetName(d.Name()), "]")
	return d, nil
}

//
// SetupIFace
// @Description:
// @param cfg
// @param dev
//
func SetupIFace(cfg config.Config, dev *water.Interface) error {
	ip, ipNet, err := net.ParseCIDR(cfg.Device.CIDR)
	if HasSameIp(cfg.Device.CIDR) {
		return errors.New("ip address is already in use : " + cfg.Device.CIDR)
	}
	mask := utils.CIDR2Mask(ipNet.Mask)
	if err != nil {
		return err
	}
	//mtu := cfg.Global.MTU - 100
	mtu := cfg.Global.MTU
	if mtu <= 0 {
		return errors.New("mtu value too low : " + strconv.Itoa(cfg.Global.MTU))
	}
	mtuStr := strconv.FormatInt(int64(mtu), 10)
	log.Info("[device:", dev.Name(), "] set mtu : ", mtuStr)
	if runtime.GOOS == "linux" {
		command("/sbin/ip", "link", "set", "dev", dev.Name(), "mtu", mtuStr)
		command("/sbin/ip", "addr", "add", cfg.Device.CIDR, "dev", dev.Name())
		command("/sbin/ip", "link", "set", "dev", dev.Name(), "up")
	} else if runtime.GOOS == "windows" {
		//netsh interface ip set address Name="以太网 4" source=static addr=192.168.100.9 mask=255.255.255.0 store=active
		command("PowerShell", "netsh", "interface", "ip", "set", "address", "Name=\""+dev.Name()+"\"", "source=static", "addr="+ip.String(), "mask="+mask, "store=active")
		//netsh interface ipv4 set interface mtu
		command("PowerShell", "netsh", "interface", "ipv4", "set", "interface", "\""+dev.Name()+"\"", "mtu="+mtuStr)
	} else {
		return errors.New("unsupported platform " + runtime.GOOS)
	}
	log.Info("network interface setup success...")
	return nil
}

//
// command
// @Description:
// @param c
// @param args
//
func command(c string, args ...string) {
	cmd := exec.Command(c, args...)
	//_ = cmd.StartServ()
	//log.Info("exec --> ", cmd.String())
	err := cmd.Run()
	if err != nil {
		_ = log.Warn("exec failed : ", err.Error())
	}
}

//
// GetMacByInetName
// @Description:
// @param Name
//
func GetMacByInetName(name string) string {
	interfaces, err := net.Interfaces()
	if err != nil {
		return ""
	}
	for _, inter := range interfaces {
		if inter.Name == name {
			return strings.ToLower(inter.HardwareAddr.String())
		}
	}
	return ""
}

//
// HasSameIp
// @Description:
// @param Name
//
func HasSameIp(ip string) bool {
	interfaces, err := net.Interfaces()
	if err != nil {
		return true
	}
	for _, inter := range interfaces {
		if addrs, err := inter.Addrs(); err != nil {
			return true
		} else {
			for i := range addrs {
				if addrs[i].String() == ip {
					log.Info(inter.Name, " ip duplicate")
					return true
				}
			}
		}
	}
	return false
}
